/**************************************************************************
Step 1. Include necessary header files such that the stuff your test logic needs is declared. 
Don't forget gtest.h, which declares the testing framework.                                                   
**************************************************************************/
#include "gtest/gtest.h"
#include "include/pf_palloc.h"


/**************************************************************************
Step 2. Use test fixture to design your complex cases. 
You can add SetUp() to initialize the test data and 
TearDown() to clear the test data for common usage
Pls remember that when you using test fixture, need to use macro "TEST_F" instead of "TEST"                                                 
**************************************************************************/
class PfPallocTest : public testing::Test 
{
    /* This SetUp() will be called before every TEST CASE which is marked by macro "TEST_F" */
    virtual void SetUp() 
    {

    }

    /* This TearDown will be called after every TEST CASE which is marked by macro "TEST_F" */
    virtual void TearDown() 
    {

    }

};

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
class PfPallocDeathTest : public testing::Test 
{
    /* This SetUp() will be called before every TEST CASE which is marked by macro "TEST_F" */
    virtual void SetUp() 
    {

    }

    /* This TearDown will be called after every TEST CASE which is marked by macro "TEST_F" */
    virtual void TearDown() 
    {

    }

};
#endif /* #ifndef VALGRIND_CHECK */

typedef struct pf_pool_large 
{
    struct pf_pool_large*    next;
    void*                    alloc;
} pf_pool_large_t;

typedef struct pf_pool_data
{
    uint8_t*              last;
    uint8_t*              end;
    pf_pool_t*            next;
    uint32_t              failed;
} pf_pool_data_t;


struct pf_pool 
{
    pf_pool_data_t      d;
    size_t              max;
    pf_pool_t*          current;
    pf_pool_large_t*    large;
    pf_pool_cleanup_t*  cleanup;
};

typedef void (*pf_pool_cleanup_f)(void *data);
struct pf_pool_cleanup 
{
    pf_pool_cleanup_f          handler;
    void*                      data;
    struct pf_pool_cleanup*    next;
};

typedef struct Big
{
    char buf[4096];
}Big_t;

typedef struct Small
{
    char buf[512];
}Small_t;

/***************************************************************************
  Step 3. Use macro "TEST_F" to start your test case                                      
  You can design your test case as the example
 ***************************************************************************/
TEST_F(PfPallocTest, pf_pool_create_normal_test)
{
    /* Define the local variables */
    int i;

    /* initilize test data for this case */

    size_t size;
    pf_pool_t *pool[5];


    /* Call your function for test */

    size    = 0;
    pool[0] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[0]);

    size    = 1024;
    pool[1] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[1]);

    size    = 1024*1024;
    pool[2] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[2]);

    size    = 1024*1024*1024;
    pool[3] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[3]);


    /* Clear the test data for this case */
    for(i=0;i<4;i++)
    {
        if(pool[i] != NULL) 
        {
            pf_pool_destroy(pool[i]); 
        }
    }
}

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
TEST_F(PfPallocDeathTest, pf_pool_destroy_dead_test)
{
    EXPECT_DEBUG_DEATH(pf_pool_destroy((pf_pool_t *)NULL),"");
}
#endif /* #ifndef VALGRIND_CHECK */

TEST_F(PfPallocTest, pf_palloc_normal_test)
{
    int i;
    size_t size;
    pf_pool_t *pool[2];
    Small_t *small[5];
    Big_t * big[5];
    void *temp;

    size    = 1024;
    pool[0] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[0]);

    size    = 8192;
    pool[1] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[1]);

    size = sizeof(Small_t);
    for(i=0;i<5;i++)
    {
        small[i] = (Small_t *)pf_palloc(pool[0],size);
        EXPECT_NE((Small_t*)NULL,small[i]);
        EXPECT_EQ((size_t)512,sizeof(*small[i]));
        EXPECT_EQ((pf_pool_large_t*)NULL,pool[0]->large);
    }

    size = sizeof(Big_t);
    for(i=0;i<5;i++)
    {
        big[i] = (Big_t *)pf_palloc(pool[1],size);
        EXPECT_NE((Big_t*)NULL,big[i]);
        EXPECT_EQ((size_t)4096,sizeof(*big[i]));
        if (pool[1]->large->alloc == big[i]) 
        {
            EXPECT_EQ(1,1)<<"i:"<<i; 
        }
        else
        {
            EXPECT_EQ(1,0)<<"i:"<<i; 
        }
    }

    pf_pfree(pool[1],big[4]);   
    pf_pfree(pool[1],big[0]);   
    size = sizeof(Big_t);
    for(i=0;i<5;i++)
    {
        big[i] = (Big_t *)pf_palloc(pool[1],size);
        EXPECT_NE((Big_t*)NULL,big[i]);
        EXPECT_EQ((size_t)4096,sizeof(*big[i]));
    }

    size = 768;
    for(i=0;i<10;i++)
    {
        temp = pf_palloc(pool[0],size);
        EXPECT_NE((void*)NULL,temp);
    }

    pf_pool_reset(pool[0]);
    pf_pool_reset(pool[1]);

    pf_pool_destroy(pool[0]);
    pf_pool_destroy(pool[1]);

    size    = 4;
    pool[0] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[0]);

    size   = 16;
    big[0] = (Big_t *)pf_palloc(pool[0],size);
    EXPECT_NE((Big_t*)NULL,big[0]);

    //-1 will turn to 18446744073709551615 ,malloc failed
    small[0] = (Small_t *)pf_palloc(pool[0],-1);
    EXPECT_EQ((Small_t*)NULL,small[0]);

    //size = 0
    small[0] = (Small_t *)pf_palloc(pool[0],0);
    EXPECT_EQ((Small_t*)NULL,small[0]);

    pf_pool_reset(pool[0]);
    pf_pool_destroy(pool[0]);

}

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
TEST_F(PfPallocDeathTest, pf_palloc_dead_test)
{
    EXPECT_DEBUG_DEATH(pf_palloc((pf_pool_t *)NULL,1024),"");
}

TEST_F(PfPallocDeathTest, pf_pool_reset_dead_test)
{
    EXPECT_DEBUG_DEATH(pf_pool_reset((pf_pool_t *)NULL),"");
}
#endif /* #ifndef VALGRIND_CHECK */

TEST_F(PfPallocTest, pf_pnalloc_normal_test)
{
    int i;
    size_t size;
    pf_pool_t *pool[2];
    Small_t *small[5];
    Big_t * big[5];

    size    = 1024;
    pool[0] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[0]);

    size    = 8192;
    pool[1] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[1]);

    size = sizeof(Small_t);
    for(i=0;i<5;i++)
    {
        small[i] = (Small_t *)pf_pnalloc(pool[0],size);
        EXPECT_NE((Small_t*)NULL,small[i]);
        EXPECT_EQ((size_t)512,sizeof(*small[i]));
        EXPECT_EQ((pf_pool_large_t*)NULL,pool[0]->large);
    }

    size = sizeof(Big_t);
    for(i=0;i<5;i++)
    {
        big[i] = (Big_t *)pf_pnalloc(pool[1],size);
        EXPECT_NE((Big_t*)NULL,big[i]);
        EXPECT_EQ((size_t)4096,sizeof(*big[i]));
        if (pool[1]->large->alloc == big[i]) 
        {
            EXPECT_EQ(1,1)<<"i:"<<i; 
        }
        else
        {
            EXPECT_EQ(1,0)<<"i:"<<i; 
        }
    }


    for(i=0;i<5;i++)
    {
        pf_pfree(pool[0],small[i]);   
        pf_pfree(pool[1],big[i]);   
    }

    //size = -1 will turn to 18446744073709551615 ,malloc failed
    small[0] = (Small_t *)pf_pnalloc(pool[0],-1);
    EXPECT_EQ((Small_t*)NULL,small[0]);

    //size = 0
    small[0] = (Small_t *)pf_pnalloc(pool[0],0);
    EXPECT_EQ((Small_t*)NULL,small[0]);

    pf_pool_destroy(pool[0]);
    pf_pool_destroy(pool[1]);

}

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
TEST_F(PfPallocDeathTest, pf_pnalloc_dead_test)
{
    EXPECT_DEBUG_DEATH(pf_pnalloc((pf_pool_t *)NULL,1024),"");
}

TEST_F(PfPallocDeathTest, pf_pfree_dead_test)
{
    pf_pool_t *pool;
    Small_t *small;
    size_t size;

    size = 1024;
    pool = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool);

    small = (Small_t *)pf_pnalloc(pool,size);
    EXPECT_NE((Small_t*)NULL,small);

    EXPECT_DEBUG_DEATH(pf_pfree((pf_pool_t *)NULL,small),"");
    EXPECT_DEBUG_DEATH(pf_pfree((pf_pool_t *)NULL,(Small_t *)NULL),"");

    pf_pool_destroy(pool);
}
#endif /* #ifndef VALGRIND_CHECK */

TEST_F(PfPallocTest, pf_pcalloc_normal_test)
{
    int i;
    size_t size;
    pf_pool_t *pool[2] = {NULL};
    Small_t *small[5]  = {NULL};
    Big_t * big[5]     = {NULL};

    size    = 1024;
    pool[0] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[0]);

    size    = 8192;
    pool[1] = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool[1]);

    size = sizeof(Small_t);
    for(i=0;i<5;i++)
    {
        small[i] = (Small_t *)pf_pcalloc(pool[0],size);
        EXPECT_NE((Small_t*)NULL,small[i]);
        EXPECT_EQ((size_t)512,sizeof(*small[i]));
        EXPECT_EQ((pf_pool_large_t*)NULL,pool[0]->large);
    }

    size = sizeof(Big_t);
    for(i=0;i<5;i++)
    {
        big[i] = (Big_t *)pf_pcalloc(pool[1],size);
        EXPECT_NE((Big_t*)NULL,big[i]);
        EXPECT_EQ((size_t)4096,sizeof(*big[i]));
        if (pool[1]->large->alloc == big[i]) 
        {
            EXPECT_EQ(1,1)<<"i:"<<i; 
        }
        else
        {
            EXPECT_EQ(1,0)<<"i:"<<i; 
        }
    }

    pf_pool_reset(pool[0]);
    pf_pool_reset(pool[1]);


    //size = -1 will turn to 18446744073709551615 ,malloc failed
    small[0] = (Small_t *)pf_pcalloc(pool[0],-1);
    EXPECT_EQ((Small_t*)NULL,small[0]);

    // size = 0
    small[0] = (Small_t *)pf_pcalloc(pool[0],0);
    EXPECT_EQ((Small_t*)NULL,small[0]);

    pf_pool_destroy(pool[0]);
    pf_pool_destroy(pool[1]);

}

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
TEST_F(PfPallocDeathTest, pf_pcalloc_dead_test)
{
    EXPECT_DEBUG_DEATH(pf_pcalloc((pf_pool_t *)NULL,1024),"");
}
#endif /* #ifndef VALGRIND_CHECK */

TEST_F(PfPallocTest, pf_pmemalign_normal_test)
{
    int i;
    pf_pool_t *pool = {NULL};
    Big_t * big[5]  = {NULL};
    size_t size;
    size_t alignment;

    size = 8192;
    pool = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool);

    size      = sizeof(Big_t);
    alignment = 2;
    big[0]    = (Big_t*)pf_pmemalign(pool, size, alignment);
    EXPECT_EQ((Big_t*)NULL,big[0]);

    size      = sizeof(Big_t);
    alignment = 32;
    big[1]    = (Big_t*)pf_pmemalign(pool, size, alignment);
    EXPECT_NE((Big_t*)NULL,big[1]);

    size      = sizeof(Big_t);
    alignment = 31;
    big[2]    = (Big_t*)pf_pmemalign(pool, size, alignment);
    EXPECT_EQ((Big_t*)NULL,big[2]);

    size      = 0;
    alignment = 16;
    big[3]    = (Big_t*)pf_pmemalign(pool, size, alignment);
    EXPECT_NE((Big_t*)NULL,big[3]);

    size      = -1;
    alignment = 16;
    big[4]    = (Big_t*)pf_pmemalign(pool, size, alignment);
    EXPECT_EQ((Big_t*)NULL,big[4]);


    for(i=0;i<5;i++)
    {
        if(big[i] != NULL)
            pf_pfree(pool,big[i]);   
    }
    pf_pool_destroy(pool);
}

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
TEST_F(PfPallocDeathTest, pf_pmemalign_dead_test)
{
    size_t size      = 1024;
    size_t alignment = 16;
    EXPECT_DEBUG_DEATH(pf_pmemalign((pf_pool_t *)NULL,size, alignment),"");
}
#endif /* #ifndef VALGRIND_CHECK */

TEST_F(PfPallocTest, pf_pool_cleanup_add_normal_test)
{
    pf_pool_t* pool;
    pf_pool_cleanup_t* clean;
    size_t size;

    size = 8192;
    pool = pf_pool_create(size);
    EXPECT_NE((pf_pool_t *)NULL,pool);

    clean = pf_pool_cleanup_add(pool,1024);
    EXPECT_NE((pf_pool_cleanup_t*)NULL,clean);
    clean = NULL;

    clean = pf_pool_cleanup_add(pool,10240);
    EXPECT_NE((pf_pool_cleanup_t*)NULL,clean);
    clean = NULL;

    clean = pf_pool_cleanup_add(pool,0);
    EXPECT_NE((pf_pool_cleanup_t*)NULL,clean);
    EXPECT_EQ((pf_pool_cleanup_t*)NULL,clean->data);
    clean = NULL;

    //-1 will turn to 18446744073709551615 ,malloc failed
    clean = pf_pool_cleanup_add(pool,-1);
    EXPECT_EQ((pf_pool_cleanup_t*)NULL,clean);
    clean = NULL;

    pf_pool_reset(pool);
    pf_pool_destroy(pool);
}

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
TEST_F(PfPallocDeathTest, pf_pool_cleanup_add_dead_test)
{
    EXPECT_DEBUG_DEATH(pf_pool_cleanup_add(NULL,1024),"");
}
#endif /* #ifndef VALGRIND_CHECK */
